home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-12-09 | 62.4 KB | 1,951 lines |
- ;;; -*- Package: ALIEN -*-
- ;;;
- ;;; **********************************************************************
- ;;; This code was written as part of the CMU Common Lisp project at
- ;;; Carnegie Mellon University, and has been placed in the public domain.
- ;;; If you want to use this code or any part of CMU Common Lisp, please contact
- ;;; Scott Fahlman or slisp-group@cs.cmu.edu.
- ;;;
- (ext:file-comment
- "$Header: alieneval.lisp,v 1.27 92/11/11 02:33:52 wlott Exp $")
- ;;;
- ;;; **********************************************************************
- ;;;
- ;;; This file contains any the part of the Alien implementation that
- ;;; is not part of the compiler.
- ;;;
- (in-package "ALIEN")
- (use-package "EXT")
- (use-package "SYSTEM")
-
- (export '(alien * array struct union enum function integer signed unsigned
- boolean values single-float double-float system-area-pointer
- def-alien-type def-alien-variable sap-alien
- extern-alien with-alien slot deref addr cast alien-sap alien-size
- alien-funcall def-alien-routine make-alien free-alien
- null-alien))
-
- (in-package "ALIEN-INTERNALS")
- (in-package "ALIEN")
-
- (import '(alien alien-value alien-value-type parse-alien-type
- unparse-alien-type alien-type-= alien-subtype-p alien-typep
-
- def-alien-type-class def-alien-type-translator def-alien-type-method
- invoke-alien-type-method
-
- alien-type alien-type-p alien-type-bits alien-type-alignment
- alien-integer-type alien-integer-type-p alien-integer-type-signed
- alien-boolean-type alien-boolean-type-p
- alien-enum-type alien-enum-type-p
- alien-float-type alien-float-type-p
- alien-single-float-type alien-single-float-type-p
- alien-double-float-type alien-double-float-type-p
- alien-pointer-type alien-pointer-type-p alien-pointer-type-to
- make-alien-pointer-type
- alien-array-type alien-array-type-p alien-array-type-element-type
- alien-array-type-dimensions
- alien-record-type alien-record-type-p alien-record-type-fields
- alien-record-field alien-record-field-p alien-record-field-name
- alien-record-field-type alien-record-field-offset
- alien-function-type alien-function-type-p make-alien-function-type
- alien-function-type-result-type alien-function-type-arg-types
- alien-values-type alien-values-type-p alien-values-type-values
- *values-type-okay*
-
- %set-slot %slot-addr %set-deref %deref-addr
-
- %heap-alien %set-heap-alien %heap-alien-addr
- heap-alien-info heap-alien-info-p heap-alien-info-type
- heap-alien-info-sap-form
-
- local-alien %set-local-alien %local-alien-addr
- local-alien-info local-alien-info-p local-alien-info-type
- local-alien-info-force-to-memory-p
- %local-alien-forced-to-memory-p
- make-local-alien dispose-local-alien note-local-alien-type
-
- %cast %sap-alien align-offset
-
- extract-alien-value deposit-alien-value naturalize deport
- compute-lisp-rep-type compute-alien-rep-type
- compute-extract-lambda compute-deposit-lambda
- compute-naturalize-lambda compute-deport-lambda)
- "ALIEN-INTERNALS")
-
- (export '(alien alien-value alien-value-type parse-alien-type
- unparse-alien-type alien-type-= alien-subtype-p alien-typep
-
- def-alien-type-class def-alien-type-translator def-alien-type-method
- invoke-alien-type-method
-
- alien-type alien-type-p alien-type-bits alien-type-alignment
- alien-integer-type alien-integer-type-p alien-integer-type-signed
- alien-boolean-type alien-boolean-type-p
- alien-enum-type alien-enum-type-p
- alien-float-type alien-float-type-p
- alien-single-float-type alien-single-float-type-p
- alien-double-float-type alien-double-float-type-p
- alien-pointer-type alien-pointer-type-p alien-pointer-type-to
- make-alien-pointer-type
- alien-array-type alien-array-type-p alien-array-type-element-type
- alien-array-type-dimensions
- alien-record-type alien-record-type-p alien-record-type-fields
- alien-record-field alien-record-field-p alien-record-field-name
- alien-record-field-type alien-record-field-offset
- alien-function-type alien-function-type-p make-alien-function-type
- alien-function-type-result-type alien-function-type-arg-types
- alien-values-type alien-values-type-p alien-values-type-values
- *values-type-okay*
-
- %set-slot %slot-addr %set-deref %deref-addr
-
- %heap-alien %set-heap-alien %heap-alien-addr
- heap-alien-info heap-alien-info-p heap-alien-info-type
- heap-alien-info-sap-form
-
- local-alien %set-local-alien %local-alien-addr
- local-alien-info local-alien-info-p local-alien-info-type
- local-alien-info-force-to-memory-p
- %local-alien-forced-to-memory-p
- make-local-alien dispose-local-alien note-local-alien-type
-
- %cast %sap-alien align-offset
-
- extract-alien-value deposit-alien-value naturalize deport
- compute-lisp-rep-type compute-alien-rep-type
- compute-extract-lambda compute-deposit-lambda
- compute-naturalize-lambda compute-deport-lambda)
- "ALIEN-INTERNALS")
-
-
-
- ;;;; Utility functions.
-
- (defun align-offset (offset alignment)
- (let ((extra (rem offset alignment)))
- (if (zerop extra) offset (+ offset (- alignment extra)))))
-
- (defun guess-alignment (bits)
- (cond ((null bits) nil)
- ((> bits 16) 32)
- ((> bits 8) 16)
- ((> bits 1) 8)
- (t 1)))
-
-
- ;;;; Alien-type-info stuff.
-
- (eval-when (compile eval load)
-
- (defstruct (alien-type-class
- (:print-function %print-alien-type-class))
- (name nil :type symbol)
- (include nil :type (or null alien-type-class))
- (unparse nil :type (or null function))
- (type= nil :type (or null function))
- (lisp-rep nil :type (or null function))
- (alien-rep nil :type (or null function))
- (extract-gen nil :type (or null function))
- (deposit-gen nil :type (or null function))
- (naturalize-gen nil :type (or null function))
- (deport-gen nil :type (or null function))
- ;; Cast?
- (arg-tn nil :type (or null function))
- (result-tn nil :type (or null function))
- (subtypep nil :type (or null function)))
-
- (defun %print-alien-type-class (type-class stream depth)
- (declare (ignore depth))
- (print-unreadable-object (type-class stream :type t)
- (prin1 (alien-type-class-name type-class) stream)))
-
- (defvar *alien-type-classes* (make-hash-table :test #'eq))
-
- (defun alien-type-class-or-lose (name)
- (or (gethash name *alien-type-classes*)
- (error "No alien type class ~S" name)))
-
- (defun create-alien-type-class-if-necessary (name include)
- (let ((old (gethash name *alien-type-classes*))
- (include (and include (alien-type-class-or-lose include))))
- (if old
- (setf (alien-type-class-include old) include)
- (setf (gethash name *alien-type-classes*)
- (make-alien-type-class :name name :include include)))))
-
- (defconstant method-slot-alist
- '((:unparse . alien-type-class-unparse)
- (:type= . alien-type-class-type=)
- (:subtypep . alien-type-class-subtypep)
- (:lisp-rep . alien-type-class-lisp-rep)
- (:alien-rep . alien-type-class-alien-rep)
- (:extract-gen . alien-type-class-extract-gen)
- (:deposit-gen . alien-type-class-deposit-gen)
- (:naturalize-gen . alien-type-class-naturalize-gen)
- (:deport-gen . alien-type-class-deport-gen)
- ;; Cast?
- (:arg-tn . alien-type-class-arg-tn)
- (:result-tn . alien-type-class-result-tn)))
-
- (defun method-slot (method)
- (cdr (or (assoc method method-slot-alist)
- (error "No method ~S" method))))
-
- ); eval-when
-
-
- (defmacro def-alien-type-class ((name &key include) &rest slots)
- (let ((defstruct-name
- (intern (concatenate 'string "ALIEN-" (symbol-name name) "-TYPE"))))
- (multiple-value-bind
- (include include-defstruct overrides)
- (etypecase include
- (null
- (values nil 'alien-type nil))
- (symbol
- (values
- include
- (intern (concatenate 'string
- "ALIEN-" (symbol-name include) "-TYPE"))
- nil))
- (list
- (values
- (car include)
- (intern (concatenate 'string
- "ALIEN-" (symbol-name (car include)) "-TYPE"))
- (cdr include))))
- `(progn
- (eval-when (compile load eval)
- (create-alien-type-class-if-necessary ',name ',(or include 'root)))
- (defstruct (,defstruct-name
- (:include ,include-defstruct
- (:class ',name)
- ,@overrides))
- ,@slots)))))
-
- (defmacro def-alien-type-method ((class method) lambda-list &rest body)
- (let ((defun-name (intern (concatenate 'string
- (symbol-name class)
- "-"
- (symbol-name method)
- "-METHOD"))))
- `(progn
- (defun ,defun-name ,lambda-list
- ,@body)
- (setf (,(method-slot method) (alien-type-class-or-lose ',class))
- #',defun-name))))
-
- (defmacro invoke-alien-type-method (method type &rest args)
- (let ((slot (method-slot method)))
- (once-only ((type type))
- `(funcall (do ((class (alien-type-class-or-lose (alien-type-class ,type))
- (alien-type-class-include class)))
- ((null class)
- (error "Method ~S not defined for ~S"
- ',method (alien-type-class ,type)))
- (let ((fn (,slot class)))
- (when fn
- (return fn))))
- ,type ,@args))))
-
-
-
- ;;;; Alien-type defstruct.
-
- (eval-when (compile load eval)
- (create-alien-type-class-if-necessary 'root nil))
-
- (defstruct (alien-type
- (:print-function %print-alien-type)
- (:make-load-form-fun :just-dump-it-normally))
- (class 'root :type symbol)
- (bits nil :type (or null unsigned-byte))
- (alignment (guess-alignment bits) :type (or null unsigned-byte)))
-
- (defun %print-alien-type (type stream depth)
- (declare (ignore depth))
- (print-unreadable-object (type stream :type t)
- (prin1 (unparse-alien-type type) stream)))
-
-
- ;;;; Type parsing and unparsing.
-
- (defvar *auxiliary-type-definitions* nil)
- (defvar *new-auxiliary-types*)
-
- ;;; WITH-AUXILIARY-ALIEN-TYPES -- internal.
- ;;;
- ;;; Process stuff in a new scope.
- ;;;
- (defmacro with-auxiliary-alien-types (&body body)
- `(let ((*auxiliary-type-definitions*
- (if (boundp '*new-auxiliary-types*)
- (append *new-auxiliary-types* *auxiliary-type-definitions*)
- *auxiliary-type-definitions*))
- (*new-auxiliary-types* nil))
- ,@body))
-
- ;;; PARSE-ALIEN-TYPE -- public
- ;;;
- (defun parse-alien-type (type)
- "Parse the list structure TYPE as an alien type specifier and return
- the resultant alien-type structure."
- (if (boundp '*new-auxiliary-types*)
- (%parse-alien-type type)
- (let ((*new-auxiliary-types* nil))
- (%parse-alien-type type))))
-
- (defun %parse-alien-type (type)
- (if (consp type)
- (let ((translator (info alien-type translator (car type))))
- (unless translator
- (error "Unknown alien type: ~S" type))
- (funcall translator type))
- (case (info alien-type kind type)
- (:primitive
- (let ((translator (info alien-type translator type)))
- (unless translator
- (error "No translator for primitive alien type ~S?" type))
- (funcall translator (list type))))
- (:defined
- (or (info alien-type definition type)
- (error "Definition missing for alien type ~S?" type)))
- (:unknown
- (error "Unknown alien type: ~S" type)))))
-
- (defun auxiliary-alien-type (kind name)
- (flet ((aux-defn-matches (x)
- (and (eq (first x) kind) (eq (second x) name))))
- (let ((in-auxiliaries
- (or (find-if #'aux-defn-matches *new-auxiliary-types*)
- (find-if #'aux-defn-matches *auxiliary-type-definitions*))))
- (if in-auxiliaries
- (values (third in-auxiliaries) t)
- (ecase kind
- (:struct
- (info alien-type struct name))
- (:union
- (info alien-type union name))
- (:enum
- (info alien-type enum name)))))))
-
- (defun %set-auxiliary-alien-type (kind name defn)
- (flet ((aux-defn-matches (x)
- (and (eq (first x) kind) (eq (second x) name))))
- (when (find-if #'aux-defn-matches *new-auxiliary-types*)
- (error "Attempt to multiple define ~A ~S." kind name))
- (when (find-if #'aux-defn-matches *auxiliary-type-definitions*)
- (error "Attempt to shadow definition of ~A ~S." kind name)))
- (push (list kind name defn) *new-auxiliary-types*)
- defn)
-
- (defsetf auxiliary-alien-type %set-auxiliary-alien-type)
-
- (defun verify-local-auxiliaries-okay ()
- (dolist (info *new-auxiliary-types*)
- (destructuring-bind (kind name defn) info
- (declare (ignore defn))
- (when (ecase kind
- (:struct
- (info alien-type struct name))
- (:union
- (info alien-type union name))
- (:enum
- (info alien-type enum name)))
- (error "Attempt to shadow definition of ~A ~S." kind name)))))
-
- ;;; *record-type-already-unparsed* -- internal
- ;;;
- ;;; Holds the list of record types that have already been unparsed. This is
- ;;; used to keep from outputing the slots again if the same structure shows
- ;;; up twice.
- ;;;
- (defvar *record-types-already-unparsed*)
-
- ;;; UNPARSE-ALIEN-TYPE -- public.
- ;;;
- (defun unparse-alien-type (type)
- "Convert the alien-type structure TYPE back into a list specification of
- the type."
- (declare (type alien-type type))
- (let ((*record-types-already-unparsed* nil))
- (%unparse-alien-type type)))
-
- ;;; %UNPARSE-ALIEN-TYPE -- internal.
- ;;;
- ;;; Does all the work of UNPARSE-ALIEN-TYPE. It's seperate because we need
- ;;; to recurse inside the binding of *record-types-already-unparsed*.
- ;;;
- (defun %unparse-alien-type (type)
- (invoke-alien-type-method :unparse type))
-
-
-
-
- ;;;; Alien type defining stuff.
-
- (defmacro def-alien-type-translator (name lambda-list &body body)
- (let ((whole (gensym))
- (defun-name (intern (concatenate 'string
- "ALIEN-"
- (symbol-name name)
- "-TYPE-TRANSLATOR"))))
- (multiple-value-bind
- (body decls docs)
- (lisp::parse-defmacro lambda-list whole body name
- 'def-alien-type-translator)
- `(progn
- (defun ,defun-name (,whole)
- ,decls
- (block ,name
- ,body))
- (%def-alien-type-translator ',name #',defun-name ,docs)))))
-
- (defun %def-alien-type-translator (name translator docs)
- (declare (ignore docs))
- (setf (info alien-type kind name) :primitive)
- (setf (info alien-type translator name) translator)
- (clear-info alien-type definition name)
- #+nil
- (setf (documentation name 'alien-type) docs)
- name)
-
-
- (defmacro def-alien-type (name type)
- "Define the alien type NAME to be equivalent to TYPE. Name may be NIL for
- STRUCT and UNION types, in which case the name is taken from the type
- specifier."
- (with-auxiliary-alien-types
- (let ((alien-type (parse-alien-type type)))
- `(eval-when (compile load eval)
- ,@(when *new-auxiliary-types*
- `((%def-auxiliary-alien-types ',*new-auxiliary-types*)))
- ,@(when name
- `((%def-alien-type ',name ',alien-type)))))))
-
- (defun %def-auxiliary-alien-types (types)
- (dolist (info types)
- (destructuring-bind (kind name defn) info
- (macrolet ((frob (kind)
- `(let ((old (info alien-type ,kind name)))
- (unless (or (null old) (alien-type-= old defn))
- (warn "Redefining ~A ~S to be:~% ~S,~%was:~% ~S"
- kind name defn old))
- (setf (info alien-type ,kind name) defn))))
- (ecase kind
- (:struct (frob struct))
- (:union (frob union))
- (:enum (frob enum)))))))
-
- (defun %def-alien-type (name new)
- (ecase (info alien-type kind name)
- (:primitive
- (error "~S is a built-in alien type." name))
- (:defined
- (let ((old (info alien-type definition name)))
- (unless (or (null old) (alien-type-= new old))
- (warn "Redefining ~S to be:~% ~S,~%was~% ~S" name
- (unparse-alien-type new) (unparse-alien-type old)))))
- (:unknown))
- (setf (info alien-type definition name) new)
- (setf (info alien-type kind name) :defined)
- name)
-
-
-
- ;;;; Interfaces to the different methods
-
- (defun alien-type-= (type1 type2)
- "Return T iff TYPE1 and TYPE2 describe equivalent alien types."
- (or (eq type1 type2)
- (and (eq (alien-type-class type1)
- (alien-type-class type2))
- (invoke-alien-type-method :type= type1 type2))))
-
- (defun alien-subtype-p (type1 type2)
- "Return T iff the alien type TYPE1 is a subtype of TYPE2. Currently, the
- only supported subtype relationships are is that any pointer type is a
- subtype of (* t), and any array type first dimension will match
- (array <eltype> nil ...). Otherwise, the two types have to be
- ALIEN-TYPE-=."
- (or (eq type1 type2)
- (invoke-alien-type-method :subtypep type1 type2)))
-
- (defun alien-typep (object type)
- "Return T iff OBJECT is an alien of type TYPE."
- (let ((lisp-rep-type (compute-lisp-rep-type type)))
- (if lisp-rep-type
- (typep object lisp-rep-type)
- (and (alien-value-p object)
- (alien-subtype-p (alien-value-type object) type)))))
-
-
- (defun compute-naturalize-lambda (type)
- `(lambda (alien ignore)
- (declare (ignore ignore))
- ,(invoke-alien-type-method :naturalize-gen type 'alien)))
-
- (defun compute-deport-lambda (type)
- (declare (type alien-type type))
- (multiple-value-bind
- (form value-type)
- (invoke-alien-type-method :deport-gen type 'value)
- `(lambda (value ignore)
- (declare (type ,(or value-type
- (compute-lisp-rep-type type)
- `(alien ,type))
- value)
- (ignore ignore))
- ,form)))
-
- (defun compute-extract-lambda (type)
- `(lambda (sap offset ignore)
- (declare (type system-area-pointer sap)
- (type unsigned-byte offset)
- (ignore ignore))
- (naturalize ,(invoke-alien-type-method :extract-gen type 'sap 'offset)
- ',type)))
-
- (defun compute-deposit-lambda (type)
- (declare (type alien-type type))
- `(lambda (sap offset ignore value)
- (declare (type system-area-pointer sap)
- (type unsigned-byte offset)
- (ignore ignore))
- (let ((value (deport value ',type)))
- ,(invoke-alien-type-method :deposit-gen type 'sap 'offset 'value)
- ;; Note: the reason we don't just return the pre-deported value
- ;; is because that would inhibit any (deport (naturalize ...))
- ;; optimizations that might have otherwise happen. Re-naturalizing
- ;; the value might cause extra consing, but is flushable, so probably
- ;; results in better code.
- (naturalize value ',type))))
-
- (defun compute-lisp-rep-type (type)
- (invoke-alien-type-method :lisp-rep type))
-
- (defun compute-alien-rep-type (type)
- (invoke-alien-type-method :alien-rep type))
-
-
-
-
-
- ;;;; Default methods.
-
- (def-alien-type-method (root :unparse) (type)
- `(!!unknown-alien-type!! ,(type-of type)))
-
- (def-alien-type-method (root :type=) (type1 type2)
- (declare (ignore type1 type2))
- t)
-
- (def-alien-type-method (root :subtypep) (type1 type2)
- (alien-type-= type1 type2))
-
- (def-alien-type-method (root :lisp-rep) (type)
- (declare (ignore type))
- nil)
-
- (def-alien-type-method (root :alien-rep) (type)
- (declare (ignore type))
- '*)
-
- (def-alien-type-method (root :naturalize-gen) (type alien)
- (declare (ignore alien))
- (error "Cannot represent ~S typed aliens." type))
-
- (def-alien-type-method (root :deport-gen) (type object)
- (declare (ignore object))
- (error "Cannot represent ~S typed aliens." type))
-
- (def-alien-type-method (root :extract-gen) (type sap offset)
- (declare (ignore sap offset))
- (error "Cannot represent ~S typed aliens." type))
-
- (def-alien-type-method (root :deposit-gen) (type sap offset value)
- `(setf ,(invoke-alien-type-method :extract-gen type sap offset) ,value))
-
- (def-alien-type-method (root :arg-tn) (type state)
- (declare (ignore state))
- (error "Cannot pass aliens of type ~S as arguments to call-out"
- (unparse-alien-type type)))
-
- (def-alien-type-method (root :result-tn) (type state)
- (declare (ignore state))
- (error "Cannot return aliens of type ~S from call-out"
- (unparse-alien-type type)))
-
-
- ;;;; The INTEGER type.
-
- (def-alien-type-class (integer)
- (signed t :type (member t nil)))
-
- (def-alien-type-translator signed (&optional (bits vm:word-bits))
- (make-alien-integer-type :bits bits))
-
- (def-alien-type-translator integer (&optional (bits vm:word-bits))
- (make-alien-integer-type :bits bits))
-
- (def-alien-type-translator unsigned (&optional (bits vm:word-bits))
- (make-alien-integer-type :bits bits :signed nil))
-
- (def-alien-type-method (integer :unparse) (type)
- (list (if (alien-integer-type-signed type) 'signed 'unsigned)
- (alien-integer-type-bits type)))
-
- (def-alien-type-method (integer :type=) (type1 type2)
- (and (eq (alien-integer-type-signed type1)
- (alien-integer-type-signed type2))
- (= (alien-integer-type-bits type1)
- (alien-integer-type-bits type2))))
-
- (def-alien-type-method (integer :lisp-rep) (type)
- (list (if (alien-integer-type-signed type) 'signed-byte 'unsigned-byte)
- (alien-integer-type-bits type)))
-
- (def-alien-type-method (integer :alien-rep) (type)
- (list (if (alien-integer-type-signed type) 'signed-byte 'unsigned-byte)
- (alien-integer-type-bits type)))
-
- (def-alien-type-method (integer :naturalize-gen) (type alien)
- (declare (ignore type))
- alien)
-
- (def-alien-type-method (integer :deport-gen) (type value)
- (declare (ignore type))
- value)
-
- (def-alien-type-method (integer :extract-gen) (type sap offset)
- (declare (type alien-integer-type type))
- (let ((ref-fun
- (if (alien-integer-type-signed type)
- (case (alien-integer-type-bits type)
- (8 'signed-sap-ref-8)
- (16 'signed-sap-ref-16)
- (32 'signed-sap-ref-32))
- (case (alien-integer-type-bits type)
- (8 'sap-ref-8)
- (16 'sap-ref-16)
- (32 'sap-ref-32)))))
- (if ref-fun
- `(,ref-fun ,sap (/ ,offset vm:byte-bits))
- (error "Cannot extract ~D bit integers."
- (alien-integer-type-bits type)))))
-
-
-
- ;;;; The BOOLEAN type.
-
- (def-alien-type-class (boolean :include integer))
-
- (def-alien-type-translator boolean (&optional (bits vm:word-bits))
- (make-alien-boolean-type :bits bits :signed nil))
-
- (def-alien-type-method (boolean :unparse) (type)
- `(boolean ,(alien-boolean-type-bits type)))
-
- (def-alien-type-method (boolean :lisp-rep) (type)
- (declare (ignore type))
- `(member t nil))
-
- (def-alien-type-method (boolean :naturalize-gen) (type alien)
- (declare (ignore type))
- `(not (zerop ,alien)))
-
- (def-alien-type-method (boolean :deport-gen) (value type)
- (declare (ignore type))
- `(if ,value 1 0))
-
-
- ;;;; The ENUM type.
-
- (def-alien-type-class (enum :include (integer (:bits 32)))
- name ; name of this enum (if any)
- from ; alist from keywords to integers.
- to ; alist or vector from integers to keywords.
- kind ; Kind of from mapping, :vector or :alist.
- offset) ; Offset to add to value for :vector from mapping.
-
- (def-alien-type-translator enum (&whole type name &rest mappings)
- (cond (mappings
- (let ((result (parse-enum name mappings)))
- (when name
- (multiple-value-bind
- (old old-p)
- (auxiliary-alien-type :enum name)
- (when old-p
- (unless (alien-type-= result old)
- (warn "Redefining alien enum ~S" name))))
- (setf (auxiliary-alien-type :enum name) result))
- result))
- (name
- (multiple-value-bind
- (result found)
- (auxiliary-alien-type :enum name)
- (unless found
- (error "Unknown enum type: ~S" name))
- result))
- (t
- (error "Empty enum type: ~S" type))))
-
- (defun parse-enum (name elements)
- (when (null elements)
- (error "An anumeration must contain at least one element."))
- (let ((min nil)
- (max nil)
- (from-alist ())
- (prev -1))
- (declare (list from-alist))
- (dolist (el elements)
- (multiple-value-bind
- (sym val)
- (if (listp el)
- (values (first el) (second el))
- (values el (1+ prev)))
- (setf prev val)
- (unless (keywordp sym)
- (error "Enumeration element ~S is not a keyword." sym))
- (unless (integerp val)
- (error "Element value ~S is not an integer." val))
- (unless (and max (> max val)) (setq max val))
- (unless (and min (< min val)) (setq min val))
- (when (rassoc val from-alist)
- (error "Element value ~S used more than once." val))
- (when (assoc sym from-alist :test #'eq)
- (error "Enumeration element ~S used more than once." sym))
- (push (cons sym val) from-alist)))
- (let* ((signed (minusp min))
- (min-bits (if signed
- (1+ (max (integer-length min)
- (integer-length max)))
- (integer-length max))))
- (when (> min-bits 32)
- (error "Can't represent enums needing more than 32 bits."))
- (setf from-alist (sort from-alist #'< :key #'cdr))
- (cond
- ;;
- ;; If range is at least 20% dense, use vector mapping. Crossover
- ;; point solely on basis of space would be 25%. Vector mapping
- ;; is always faster, so give the benefit of the doubt.
- ((< 0.2 (/ (float (length from-alist)) (float (- max min))))
- ;;
- ;; If offset is small and ignorable, ignore it to save time.
- (when (< 0 min 10) (setq min 0))
- (let ((to (make-array (1+ (- max min)))))
- (dolist (el from-alist)
- (setf (svref to (- (cdr el) min)) (car el)))
- (make-alien-enum-type :name name :signed signed
- :from from-alist :to to :kind
- :vector :offset (- min))))
- (t
- (make-alien-enum-type :name name :signed signed
- :from from-alist
- :to (mapcar #'(lambda (x) (cons (cdr x) (car x)))
- from-alist)
- :kind :alist))))))
-
- (def-alien-type-method (enum :unparse) (type)
- `(enum ,(alien-enum-type-name type)
- ,@(let ((prev -1))
- (mapcar #'(lambda (mapping)
- (let ((sym (car mapping))
- (value (cdr mapping)))
- (prog1
- (if (= (1+ prev) value)
- sym
- `(,sym ,value))
- (setf prev value))))
- (alien-enum-type-from type)))))
-
- (def-alien-type-method (enum :type=) (type1 type2)
- (and (eq (alien-enum-type-name type1)
- (alien-enum-type-name type2))
- (equal (alien-enum-type-from type1)
- (alien-enum-type-from type2))))
-
- (def-alien-type-method (enum :lisp-rep) (type)
- `(member ,@(mapcar #'car (alien-enum-type-from type))))
-
- (def-alien-type-method (enum :naturalize-gen) (type alien)
- (ecase (alien-enum-type-kind type)
- (:vector
- `(svref ',(alien-enum-type-to type)
- (+ ,alien ,(alien-enum-type-offset type))))
- (:alist
- `(ecase ,alien
- ,@(mapcar #'(lambda (mapping)
- `(,(car mapping) ,(cdr mapping)))
- (alien-enum-type-to type))))))
-
- (def-alien-type-method (enum :deport-gen) (type value)
- `(ecase ,value
- ,@(mapcar #'(lambda (mapping)
- `(,(car mapping) ,(cdr mapping)))
- (alien-enum-type-from type))))
-
-
-
- ;;;; the FLOAT types.
-
- (def-alien-type-class (float)
- (type (required-argument) :type symbol))
-
- (def-alien-type-method (float :unparse) (type)
- (alien-float-type-type type))
-
- (def-alien-type-method (float :lisp-rep) (type)
- (alien-float-type-type type))
-
- (def-alien-type-method (float :alien-rep) (type)
- (alien-float-type-type type))
-
- (def-alien-type-method (float :naturalize-gen) (type alien)
- (declare (ignore type))
- alien)
-
- (def-alien-type-method (float :deport-gen) (type value)
- (declare (ignore type))
- value)
-
-
- (def-alien-type-class (single-float :include (float (:bits 32))))
-
- (def-alien-type-translator single-float ()
- (make-alien-single-float-type :type 'single-float))
-
- (def-alien-type-method (single-float :extract-gen) (type sap offset)
- (declare (ignore type))
- `(sap-ref-single ,sap (/ ,offset vm:byte-bits)))
-
-
- (def-alien-type-class (double-float :include (float (:bits 64))))
-
- (def-alien-type-translator double-float ()
- (make-alien-double-float-type :type 'double-float))
-
- (def-alien-type-method (double-float :extract-gen) (type sap offset)
- (declare (ignore type))
- `(sap-ref-double ,sap (/ ,offset vm:byte-bits)))
-
-
-
- ;;;; The SAP type
-
- (def-alien-type-class (system-area-pointer))
-
- (def-alien-type-translator system-area-pointer ()
- (make-alien-system-area-pointer-type :bits vm:word-bits))
-
- (def-alien-type-method (system-area-pointer :unparse) (type)
- (declare (ignore type))
- 'system-area-pointer)
-
- (def-alien-type-method (system-area-pointer :lisp-rep) (type)
- (declare (ignore type))
- 'system-area-pointer)
-
- (def-alien-type-method (system-area-pointer :alien-rep) (type)
- (declare (ignore type))
- 'system-area-pointer)
-
- (def-alien-type-method (system-area-pointer :naturalize-gen) (type alien)
- (declare (ignore type))
- alien)
-
- (def-alien-type-method (system-area-pointer :deport-gen) (type object)
- (declare (ignore type))
- object)
-
- (def-alien-type-method (system-area-pointer :extract-gen) (type sap offset)
- (declare (ignore type))
- `(sap-ref-sap ,sap (/ ,offset vm:byte-bits)))
-
-
- ;;;; the ALIEN-VALUE type.
-
- (def-alien-type-class (alien-value :include system-area-pointer))
-
- (def-alien-type-method (alien-value :lisp-rep) (type)
- (declare (ignore type))
- nil)
-
- (def-alien-type-method (alien-value :naturalize-gen) (type alien)
- `(%sap-alien ,alien ',type))
-
- (def-alien-type-method (alien-value :deport-gen) (type value)
- (declare (ignore type))
- `(alien-sap ,value))
-
-
-
- ;;;; The POINTER type.
-
- (def-alien-type-class (pointer :include (alien-value (:bits vm:word-bits)))
- (to nil :type (or alien-type null)))
-
- (def-alien-type-translator * (to)
- (make-alien-pointer-type :to (if (eq to t) nil (parse-alien-type to))))
-
- (def-alien-type-method (pointer :unparse) (type)
- (let ((to (alien-pointer-type-to type)))
- `(* ,(if to
- (%unparse-alien-type to)
- t))))
-
- (def-alien-type-method (pointer :type=) (type1 type2)
- (let ((to1 (alien-pointer-type-to type1))
- (to2 (alien-pointer-type-to type2)))
- (if to1
- (if to2
- (alien-type-= to1 to2)
- nil)
- (null to2))))
-
- (def-alien-type-method (pointer :subtypep) (type1 type2)
- (and (alien-pointer-type-p type2)
- (let ((to1 (alien-pointer-type-to type1))
- (to2 (alien-pointer-type-to type2)))
- (if to1
- (if to2
- (alien-subtype-p to1 to2)
- t)
- nil))))
-
- (def-alien-type-method (pointer :deport-gen) (type value)
- (values
- `(etypecase ,value
- (null
- (int-sap 0))
- (system-area-pointer
- ,value)
- ((alien ,type)
- (alien-sap ,value)))
- `(or null system-area-pointer (alien ,type))))
-
-
- ;;;; The MEM-BLOCK type.
-
- (def-alien-type-class (mem-block :include alien-value))
-
- (def-alien-type-method (mem-block :extract-gen) (type sap offset)
- (declare (ignore type))
- `(sap+ ,sap (/ ,offset vm:byte-bits)))
-
- (def-alien-type-method (mem-block :deposit-gen) (type sap offset value)
- (let ((bits (alien-mem-block-type-bits type)))
- (unless bits
- (error "Cannot deposit aliens of type ~S (unknown size)." type))
- `(kernel:system-area-copy ,value 0 ,sap ,offset ',bits)))
-
-
- ;;;; The ARRAY type.
-
- (def-alien-type-class (array :include mem-block)
- (element-type (required-argument) :type alien-type)
- (dimensions (required-argument) :type list))
-
- (def-alien-type-translator array (ele-type &rest dims)
- (when dims
- (unless (typep (first dims) '(or kernel:index null))
- (error "First dimension is not a non-negative fixnum or NIL: ~S"
- (first dims)))
- (let ((loser (find-if-not #'(lambda (x) (typep x 'kernel:index))
- (rest dims))))
- (when loser
- (error "Dimension is not a non-negative fixnum: ~S" loser))))
-
- (let ((type (parse-alien-type ele-type)))
- (make-alien-array-type
- :element-type type
- :dimensions dims
- :alignment (alien-type-alignment type)
- :bits (if (and (alien-type-bits type)
- (every #'integerp dims))
- (* (align-offset (alien-type-bits type)
- (alien-type-alignment type))
- (reduce #'* dims))))))
-
- (def-alien-type-method (array :unparse) (type)
- `(array ,(%unparse-alien-type (alien-array-type-element-type type))
- ,@(alien-array-type-dimensions type)))
-
- (def-alien-type-method (array :type=) (type1 type2)
- (and (equal (alien-array-type-dimensions type1)
- (alien-array-type-dimensions type2))
- (alien-type-= (alien-array-type-element-type type1)
- (alien-array-type-element-type type2))))
-
- (def-alien-type-method (array :subtypep) (type1 type2)
- (and (alien-array-type-p type2)
- (let ((dim1 (alien-array-type-dimensions type1))
- (dim2 (alien-array-type-dimensions type2)))
- (and (= (length dim1) (length dim2))
- (or (and dim2
- (null (car dim2))
- (equal (cdr dim1) (cdr dim2)))
- (equal dim1 dim2))
- (alien-subtype-p (alien-array-type-element-type type1)
- (alien-array-type-element-type type2))))))
-
-
- ;;;; The RECORD type.
-
- (defstruct (alien-record-field
- (:print-function %print-alien-field)
- (:make-load-form-fun :just-dump-it-normally))
- (name (required-argument) :type symbol)
- (type (required-argument) :type alien-type)
- (bits nil :type (or unsigned-byte null))
- (offset 0 :type unsigned-byte))
-
- (defun %print-alien-field (field stream depth)
- (declare (ignore depth))
- (print-unreadable-object (field stream :type t)
- (funcall (formatter "~S ~S~@[:~D~]")
- stream
- (alien-record-field-type field)
- (alien-record-field-name field)
- (alien-record-field-bits field))))
-
- (def-alien-type-class (record :include mem-block)
- (kind :struct :type (member :struct :union))
- (name nil :type (or symbol null))
- (fields nil :type list))
-
- (def-alien-type-translator struct (name &rest fields)
- (parse-alien-record-type :struct name fields))
-
- (def-alien-type-translator union (name &rest fields)
- (parse-alien-record-type :union name fields))
-
- (defun parse-alien-record-type (kind name fields)
- (if fields
- (let* ((old (and name (auxiliary-alien-type kind name)))
- (result (if (or (null old)
- (alien-record-type-fields old))
- (make-alien-record-type :name name :kind kind)
- old)))
- (when (and name (not (eq old result)))
- (setf (auxiliary-alien-type kind name) result))
- (parse-alien-record-fields result fields)
- result)
- (if name
- (or (auxiliary-alien-type kind name)
- (setf (auxiliary-alien-type kind name)
- (make-alien-record-type :name name :kind kind)))
- (make-alien-record-type :kind kind))))
-
- ;;; PARSE-ALIEN-RECORD-FIELDS -- internal
- ;;;
- ;;; Used by parse-alien-type to parse the fields of struct and union
- ;;; types. RESULT holds the record type we are paring the fields of,
- ;;; and FIELDS is the list of field specifications.
- ;;;
- (defun parse-alien-record-fields (result fields)
- (declare (type alien-record-type result)
- (type list fields))
- (let ((total-bits 0)
- (overall-alignment 1)
- (parsed-fields nil))
- (dolist (field fields)
- (destructuring-bind (var type &optional bits) field
- (declare (ignore bits))
- (let* ((field-type (parse-alien-type type))
- (bits (alien-type-bits field-type))
- (alignment (alien-type-alignment field-type))
- (parsed-field
- (make-alien-record-field :type field-type
- :name var)))
- (push parsed-field parsed-fields)
- (when (null bits)
- (error "Unknown size: ~S"
- (unparse-alien-type field-type)))
- (when (null alignment)
- (error "Unknown alignment: ~S"
- (unparse-alien-type field-type)))
- (setf overall-alignment (max overall-alignment alignment))
- (ecase (alien-record-type-kind result)
- (:struct
- (let ((offset (align-offset total-bits alignment)))
- (setf (alien-record-field-offset parsed-field) offset)
- (setf total-bits (+ offset bits))))
- (:union
- (setf total-bits (max total-bits bits)))))))
- (let ((new (nreverse parsed-fields)))
- (setf (alien-record-type-fields result) new))
- (setf (alien-record-type-alignment result) overall-alignment)
- (setf (alien-record-type-bits result)
- (align-offset total-bits overall-alignment))))
-
- (def-alien-type-method (record :unparse) (type)
- `(,(case (alien-record-type-kind type)
- (:struct 'struct)
- (:union 'union)
- (t '???))
- ,(alien-record-type-name type)
- ,@(unless (member type *record-types-already-unparsed* :test #'eq)
- (push type *record-types-already-unparsed*)
- (mapcar #'(lambda (field)
- `(,(alien-record-field-name field)
- ,(%unparse-alien-type (alien-record-field-type field))
- ,@(if (alien-record-field-bits field)
- (list (alien-record-field-bits field)))))
- (alien-record-type-fields type)))))
-
- (defun record-fields-match (fields1 fields2)
- (declare (type list fields1 fields2))
- (or (eq fields1 fields2)
- (and fields1
- fields2
- (let ((field1 (car fields1))
- (field2 (car fields2)))
- (declare (type alien-record-field field1 field2))
- (and (eq (alien-record-field-name field1)
- (alien-record-field-name field2))
- (eql (alien-record-field-bits field1)
- (alien-record-field-bits field2))
- (eql (alien-record-field-offset field1)
- (alien-record-field-offset field2))
- (alien-type-= (alien-record-field-type field1)
- (alien-record-field-type field2))))
- (record-fields-match (cdr fields1) (cdr fields2)))))
-
- (def-alien-type-method (record :type=) (type1 type2)
- (and (eq (alien-record-type-name type1)
- (alien-record-type-name type2))
- (eq (alien-record-type-kind type1)
- (alien-record-type-kind type2))
- (= (length (alien-record-type-fields type1))
- (length (alien-record-type-fields type2)))
- (record-fields-match (alien-record-type-fields type1)
- (alien-record-type-fields type2))))
-
-
- ;;;; The FUNCTION and VALUES types.
-
- (defvar *values-type-okay* nil)
-
- (def-alien-type-class (function :include mem-block)
- (result-type (required-argument) :type alien-type)
- (arg-types (required-argument) :type list)
- (stub nil :type (or null function)))
-
- (def-alien-type-translator function (result-type &rest arg-types)
- (make-alien-function-type
- :result-type (let ((*values-type-okay* t))
- (parse-alien-type result-type))
- :arg-types (mapcar #'parse-alien-type arg-types)))
-
- (def-alien-type-method (function :unparse) (type)
- `(function ,(%unparse-alien-type (alien-function-type-result-type type))
- ,@(mapcar #'%unparse-alien-type
- (alien-function-type-arg-types type))))
-
- (def-alien-type-method (function :type=) (type1 type2)
- (and (alien-type-= (alien-function-type-result-type type1)
- (alien-function-type-result-type type2))
- (= (length (alien-function-type-arg-types type1))
- (length (alien-function-type-arg-types type2)))
- (every #'alien-type-p
- (alien-function-type-arg-types type1)
- (alien-function-type-arg-types type2))))
-
-
- (def-alien-type-class (values)
- (values (required-argument) :type list))
-
- (def-alien-type-translator values (&rest values)
- (unless *values-type-okay*
- (error "Cannot use values types here."))
- (let ((*values-type-okay* nil))
- (make-alien-values-type
- :values (mapcar #'parse-alien-type values))))
-
- (def-alien-type-method (values :unparse) (type)
- `(values ,@(mapcar #'%unparse-alien-type
- (alien-values-type-values type))))
-
- (def-alien-type-method (values :type=) (type1 type2)
- (and (= (length (alien-values-type-values type1))
- (length (alien-values-type-values type2)))
- (every #'alien-type-=
- (alien-values-type-values type1)
- (alien-values-type-values type2))))
-
-
-
- ;;;; Alien variables.
-
- ;;; HEAP-ALIEN-INFO -- defstruct.
- ;;;
- ;;; Information describing a heap-allocated alien.
- ;;;
- (defstruct (heap-alien-info
- (:print-function %print-heap-alien-info)
- (:make-load-form-fun :just-dump-it-normally))
- ;; The type of this alien.
- (type (required-argument) :type alien-type)
- ;; The form to evaluate to produce the SAP pointing to where in the heap
- ;; it is.
- (sap-form (required-argument)))
- ;;;
- (defun %print-heap-alien-info (info stream depth)
- (declare (ignore depth))
- (print-unreadable-object (info stream :type t)
- (funcall (formatter "~S ~S")
- stream
- (heap-alien-info-sap-form info)
- (unparse-alien-type (heap-alien-info-type info)))))
-
- ;;; LOCAL-ALIEN-INFO -- public defstruct.
- ;;;
- ;;; Information about local aliens. The WITH-ALIEN macro builds one of these
- ;;; structures and local-alien and friends comunicate information about how
- ;;; that local alien is represented.
- ;;;
- (defstruct (local-alien-info
- (:print-function %print-local-alien-info)
- (:make-load-form-fun :just-dump-it-normally))
- ;; The type of the local alien.
- (type (required-argument) :type alien-type)
- ;; T if this local alien must be forced into memory. Using the ADDR macro
- ;; on a local alien will set this.
- (force-to-memory-p (or (alien-array-type-p type) (alien-record-type-p type))
- :type (member t nil)))
- ;;;
- (defun %print-local-alien-info (info stream depth)
- (declare (ignore depth))
- (print-unreadable-object (info stream :type t)
- (funcall (formatter "~:[~;(forced to stack) ~]~S")
- stream
- (local-alien-info-force-to-memory-p info)
- (unparse-alien-type (local-alien-info-type info)))))
-
- ;;; GUESS-ALIEN-NAME-FROM-LISP-NAME -- internal.
- ;;;
- ;;; Make a string out of the symbol, converting all uppercase letters to
- ;;; lower case and hyphens into underscores.
- ;;;
- (defun guess-alien-name-from-lisp-name (lisp-name)
- (declare (type symbol lisp-name))
- (nsubstitute #\_ #\- (string-downcase (symbol-name lisp-name))))
-
- ;;; GUESS-LISP-NAME-FROM-ALIEN-NAME -- internal.
- ;;;
- ;;; The opposite of GUESS-ALIEN-NAME-FROM-LISP-NAME. Make a symbol out of the
- ;;; string, converting all lowercase letters to uppercase and underscores into
- ;;; hyphens.
- ;;;
- (defun guess-lisp-name-from-alien-name (alien-name)
- (declare (type simple-string alien-name))
- (intern (nsubstitute #\- #\_ (string-upcase alien-name))))
-
- ;;; PICK-LISP-AND-ALIEN-NAMES -- internal.
- ;;;
- ;;; Extract the lisp and alien names from NAME. If only one is given, guess
- ;;; the other.
- ;;;
- (defun pick-lisp-and-alien-names (name)
- (etypecase name
- (string
- (values (guess-lisp-name-from-alien-name name) name))
- (symbol
- (values name (guess-alien-name-from-lisp-name name)))
- (list
- (unless (= (length name) 2)
- (error "Badly formed alien name."))
- (values (cadr name) (car name)))))
-
- ;;; DEF-ALIEN-VARIABLE -- public
- ;;;
- (defmacro def-alien-variable (name type)
- "Define NAME as an external alien variable of type TYPE. NAME should be
- a list of a symbol to use as the Lisp name, and a string holding the alien
- name. If NAME is just a symbol or string, then the other name is guessed
- from the one supplied."
- (multiple-value-bind
- (lisp-name alien-name)
- (pick-lisp-and-alien-names name)
- (with-auxiliary-alien-types
- (let ((alien-type (parse-alien-type type)))
- `(eval-when (compile load eval)
- ,@(when *new-auxiliary-types*
- `((%def-auxiliary-alien-types ',*new-auxiliary-types*)))
- (%def-alien-variable ',lisp-name
- ',alien-name
- ',alien-type))))))
-
- ;;; %DEF-ALIEN-VARIABLE -- internal
- ;;;
- ;;; Do the actual work of DEF-ALIEN-VARIABLE.
- ;;;
- (defun %def-alien-variable (lisp-name alien-name type)
- (setf (info variable kind lisp-name) :alien)
- (setf (info variable where-from lisp-name) :defined)
- (clear-info variable constant-value lisp-name)
- (setf (info variable alien-info lisp-name)
- (make-heap-alien-info :type type
- :sap-form `(foreign-symbol-address
- ',alien-name))))
-
- ;;; EXTERN-ALIEN -- public.
- ;;;
- (defmacro extern-alien (name type)
- "Access the alien variable named NAME, assuming it is of type TYPE. This
- is setfable."
- (let ((alien-name (etypecase name
- (symbol (guess-alien-name-from-lisp-name name))
- (string name))))
- `(%heap-alien ',(make-heap-alien-info
- :type (parse-alien-type type)
- :sap-form `(foreign-symbol-address ',alien-name)))))
-
- ;;; WITH-ALIEN -- public.
- ;;;
- (defmacro with-alien (bindings &body body)
- "Establish some local alien variables. Each BINDING is of the form:
- VAR TYPE [ ALLOCATION ] [ INITIAL-VALUE | EXTERNAL-NAME ]
- ALLOCATION should be one of:
- :LOCAL (the default)
- The alien is allocated on the stack, and has dynamic extent.
- :STATIC
- The alien is allocated on the heap, and has infinate extent. The alien
- is allocated at load time, so the same piece of memory is used each time
- this form executes.
- :EXTERN
- No alien is allocated, but VAR is established as a local name for
- the external alien given by EXTERNAL-NAME."
- (with-auxiliary-alien-types
- (dolist (binding (reverse bindings))
- (destructuring-bind
- (symbol type &optional (opt1 nil opt1p) (opt2 nil opt2p))
- binding
- (let ((alien-type (parse-alien-type type)))
- (multiple-value-bind
- (allocation initial-value)
- (if opt2p
- (values opt1 opt2)
- (case opt1
- (:extern
- (values opt1 (guess-alien-name-from-lisp-name symbol)))
- (:static
- (values opt1 nil))
- (t
- (values :local opt1))))
- (setf body
- (ecase allocation
- #+nil
- (:static
- (let ((sap
- (make-symbol (concatenate 'string "SAP-FOR-"
- (symbol-name symbol)))))
- `((let ((,sap (load-time-value (%make-alien ...))))
- (declare (type system-area-pointer ,sap))
- (symbol-macrolet
- ((,symbol (sap-alien ,sap ,type)))
- ,@(when initial-value
- `((setq ,symbol ,initial-value)))
- ,@body)))))
- (:extern
- (let ((info (make-heap-alien-info
- :type alien-type
- :sap-form `(foreign-symbol-address
- ',initial-value))))
- `((symbol-macrolet
- ((,symbol (%heap-alien ',info)))
- ,@body))))
- (:local
- (let ((var (gensym))
- (initval (if initial-value (gensym)))
- (info (make-local-alien-info
- :type alien-type)))
- `((let ((,var (make-local-alien ',info))
- ,@(when initial-value
- `((,initval ,initial-value))))
- (note-local-alien-type ',info ,var)
- (multiple-value-prog1
- (symbol-macrolet
- ((,symbol (local-alien ',info ,var)))
- ,@(when initial-value
- `((setq ,symbol ,initval)))
- ,@body)
- (dispose-local-alien ',info ,var))))))))))))
- (verify-local-auxiliaries-okay)
- `(compiler-let (*auxiliary-type-definitions*
- ',(append *new-auxiliary-types*
- *auxiliary-type-definitions*))
- ,@body)))
-
-
- ;;;; Runtime C values that don't correspond directly to Lisp types.
-
- ;;; ALIEN-VALUE
- ;;;
- (defstruct (alien-value
- (:print-function %print-alien-value))
- (sap (required-argument) :type system-area-pointer)
- (type (required-argument) :type alien-type))
- ;;;
- (defun %print-alien-value (value stream depth)
- (declare (ignore depth))
- (print-unreadable-object (value stream)
- (funcall (formatter "Alien ~S at #x~8,'0X")
- stream
- (unparse-alien-type (alien-value-type value))
- (sap-int (alien-value-sap value)))))
-
- (declaim (freeze-type alien-value))
-
- (declaim (inline null-alien))
- (defun null-alien (x)
- "Return true if X (which must be an Alien pointer) is null, false otherwise."
- (zerop (sap-int (alien-sap x))))
-
-
- (defmacro sap-alien (sap type)
- "Convert the System-Area-Pointer SAP to an Alien of the specified Type (not
- evaluated.) Type must be pointer-like."
- (let ((alien-type (parse-alien-type type)))
- (if (eq (compute-alien-rep-type alien-type) 'system-area-pointer)
- `(%sap-alien ,sap ',alien-type)
- (error "Cannot make aliens of type ~S out of SAPs" type))))
-
- (defun %sap-alien (sap type)
- (declare (type system-area-pointer sap)
- (type alien-type type))
- (make-alien-value :sap sap :type type))
-
- (defun alien-sap (alien)
- "Return a System-Area-Pointer pointing to Alien's data."
- (declare (type alien-value alien))
- (alien-value-sap alien))
-
-
-
- ;;;; Allocation/Deallocation of heap aliens.
-
- ;;; MAKE-ALIEN -- public.
- ;;;
- (defmacro make-alien (type &optional size)
- "Allocate an alien of type TYPE and return an alien pointer to it. If SIZE
- is supplied, how it is interpreted depends on TYPE. If TYPE is an array
- type, SIZE is used as the first dimension for the allocated array. If TYPE
- is not an array, then SIZE is the number of elements to allocate. The
- memory is allocated using ``malloc'', so it can be passed to foreign
- functions which use ``free''."
- (let ((alien-type (if (alien-type-p type) type (parse-alien-type type))))
- (multiple-value-bind
- (size-expr element-type)
- (if (alien-array-type-p alien-type)
- (let ((dims (alien-array-type-dimensions alien-type)))
- (cond
- (size
- (unless dims
- (error
- "Cannot override the size of zero-dimensional arrays."))
- (when (constantp size)
- (setf alien-type (copy-alien-array-type alien-type))
- (setf (alien-array-type-dimensions alien-type)
- (cons (eval size) (cdr dims)))))
- (dims
- (setf size (car dims)))
- (t
- (setf size 1)))
- (values `(* ,size ,@(cdr dims))
- (alien-array-type-element-type alien-type)))
- (values (or size 1) alien-type))
- (let ((bits (alien-type-bits element-type))
- (alignment (alien-type-alignment element-type)))
- (unless bits
- (error "Size of ~S unknown." (unparse-alien-type element-type)))
- (unless alignment
- (error "Alignment of ~S unknown." (unparse-alien-type element-type)))
- `(%sap-alien (%make-alien (* ,(align-offset bits alignment)
- ,size-expr))
- ',(make-alien-pointer-type :to alien-type))))))
-
- ;;; %MAKE-ALIEN -- internal
- ;;;
- ;;; Allocate a block of memory at least BITS bits long and return a system
- ;;; area pointer to it.
- ;;;
- (declaim (inline %make-alien))
- (defun %make-alien (bits)
- (declare (type kernel:index bits) (optimize-interface (safety 2)))
- (alien-funcall (extern-alien "malloc" (function system-area-pointer unsigned))
- (ash (the kernel:index (+ bits 7)) -3)))
-
- ;;; FREE-ALIEN -- public
- ;;;
- (declaim (inline free-alien))
- (defun free-alien (alien)
- "Dispose of the storage pointed to by ALIEN. ALIEN must have been allocated
- by MAKE-ALIEN or ``malloc''."
- (alien-funcall (extern-alien "free" (function (values) system-area-pointer))
- (alien-sap alien))
- nil)
-
-
- ;;;; The SLOT operator
-
- ;;; SLOT-OR-LOSE -- internal.
- ;;;
- ;;; Find the field named SLOT, or die trying.
- ;;;
- (defun slot-or-lose (type slot)
- (declare (type alien-record-type type)
- (type symbol slot))
- (or (find slot (alien-record-type-fields type)
- :key #'alien-record-field-name)
- (error "No slot named ~S in ~S" slot type)))
-
- ;;; SLOT -- public
- ;;;
- ;;; Extract the value from the named slot from the record alien. If the
- ;;; alien is actually a pointer, then deref it first.
- ;;;
- (defun slot (alien slot)
- "Extract SLOT from the Alien STRUCT or UNION ALIEN. May be set with SETF."
- (declare (type alien-value alien)
- (type symbol slot)
- (optimize (inhibit-warnings 3)))
- (let ((type (alien-value-type alien)))
- (etypecase type
- (alien-pointer-type
- (slot (deref alien) slot))
- (alien-record-type
- (let ((field (slot-or-lose type slot)))
- (extract-alien-value (alien-value-sap alien)
- (alien-record-field-offset field)
- (alien-record-field-type field)))))))
-
- ;;; %SET-SLOT -- public setf method
- ;;;
- ;;; Deposite the value in the specified slot of the record alien. If the
- ;;; alien is really a pointer, deref it first. The compiler uses this
- ;;; when it can't figure out anything better.
- ;;;
- (defun %set-slot (alien slot value)
- (declare (type alien-value alien)
- (type symbol slot)
- (optimize (inhibit-warnings 3)))
- (let ((type (alien-value-type alien)))
- (etypecase type
- (alien-pointer-type
- (%set-slot (deref alien) slot value))
- (alien-record-type
- (let ((field (slot-or-lose type slot)))
- (deposit-alien-value (alien-value-sap alien)
- (alien-record-field-offset field)
- (alien-record-field-type field)
- value))))))
- ;;;
- (defsetf slot %set-slot)
-
- ;;; %SLOT-ADDR -- internal
- ;;;
- ;;; Compute the address of the specified slot and return a pointer to it.
- ;;;
- (defun %slot-addr (alien slot)
- (declare (type alien-value alien)
- (type symbol slot)
- (optimize (inhibit-warnings 3)))
- (let ((type (alien-value-type alien)))
- (etypecase type
- (alien-pointer-type
- (%slot-addr (deref alien) slot))
- (alien-record-type
- (let* ((field (slot-or-lose type slot))
- (offset (alien-record-field-offset field))
- (field-type (alien-record-field-type field)))
- (%sap-alien (sap+ (alien-sap alien) (/ offset vm:byte-bits))
- (make-alien-pointer-type :to field-type)))))))
-
-
- ;;;; The DEREF operator.
-
- ;;; DEREF-GUTS -- internal.
- ;;;
- ;;; Does most of the work of the different DEREF methods. Returns two values:
- ;;; the type and the offset (in bits) of the refered to alien.
- ;;;
- (defun deref-guts (alien indices)
- (declare (type alien-value alien)
- (type list indices)
- (values alien-type integer))
- (let ((type (alien-value-type alien)))
- (etypecase type
- (alien-pointer-type
- (when (cdr indices)
- (error "Too many indices when derefing ~S: ~D"
- type
- (length indices)))
- (let ((element-type (alien-pointer-type-to type)))
- (values element-type
- (if indices
- (* (align-offset (alien-type-bits element-type)
- (alien-type-alignment element-type))
- (car indices))
- 0))))
- (alien-array-type
- (unless (= (length indices) (length (alien-array-type-dimensions type)))
- (error "Incorrect number of indices when derefing ~S: ~D"
- type (length indices)))
- (labels ((frob (dims indices offset)
- (if (null dims)
- offset
- (frob (cdr dims) (cdr indices)
- (+ (if (zerop offset)
- 0
- (* offset (car dims)))
- (car indices))))))
- (let ((element-type (alien-array-type-element-type type)))
- (values element-type
- (* (align-offset (alien-type-bits element-type)
- (alien-type-alignment element-type))
- (frob (alien-array-type-dimensions type)
- indices 0)))))))))
-
- ;;; DEREF -- public
- ;;;
- ;;; Dereference the alien and return the results.
- ;;;
- (defun deref (alien &rest indices)
- "De-reference an Alien pointer or array. If an array, the indices are used
- as the indices of the array element to access. If a pointer, one index can
- optionally be specified, giving the equivalent of C pointer arithmetic."
- (declare (type alien-value alien)
- (type list indices)
- (optimize (inhibit-warnings 3)))
- (multiple-value-bind
- (target-type offset)
- (deref-guts alien indices)
- (extract-alien-value (alien-value-sap alien)
- offset
- target-type)))
-
- ;;; %SET-DEREF -- public setf method
- ;;;
- (defun %set-deref (alien value &rest indices)
- (declare (type alien-value alien)
- (type list indices)
- (optimize (inhibit-warnings 3)))
- (multiple-value-bind
- (target-type offset)
- (deref-guts alien indices)
- (deposit-alien-value (alien-value-sap alien)
- offset
- target-type
- value)))
- ;;;
- (defsetf deref (alien &rest indices) (value)
- `(%set-deref ,alien ,value ,@indices))
-
- ;;; %DEREF-ADDR -- public
- ;;;
- (defun %deref-addr (alien &rest indices)
- (declare (type alien-value alien)
- (type list indices)
- (optimize (inhibit-warnings 3)))
- (multiple-value-bind
- (target-type offset)
- (deref-guts alien indices)
- (%sap-alien (sap+ (alien-value-sap alien) (/ offset vm:byte-bits))
- (make-alien-pointer-type :to target-type))))
-
-
- ;;;; Accessing heap alien variables.
-
- (defun %heap-alien (info)
- (declare (type heap-alien-info info)
- (optimize (inhibit-warnings 3)))
- (extract-alien-value (eval (heap-alien-info-sap-form info))
- 0
- (heap-alien-info-type info)))
-
- (defun %set-heap-alien (info value)
- (declare (type heap-alien-info info)
- (optimize (inhibit-warnings 3)))
- (deposit-alien-value (eval (heap-alien-info-sap-form info))
- 0
- (heap-alien-info-type info)
- value))
- ;;;
- (defsetf %heap-alien %set-heap-alien)
-
- (defun %heap-alien-addr (info)
- (declare (type heap-alien-info info)
- (optimize (inhibit-warnings 3)))
- (%sap-alien (eval (heap-alien-info-sap-form info))
- (make-alien-pointer-type :to (heap-alien-info-type info))))
-
-
-
- ;;;; Accessing local aliens.
-
- (defun make-local-alien (info)
- (let ((alien (eval `(make-alien ,(local-alien-info-type info)))))
- (finalize info #'(lambda () (free-alien alien)))
- alien))
-
- (defun note-local-alien-type (info alien)
- (declare (ignore info alien))
- nil)
-
- (defun local-alien (info alien)
- (declare (ignore info))
- (deref alien))
-
- (defun %set-local-alien (info alien value)
- (declare (ignore info))
- (setf (deref alien) value))
-
- (define-setf-method local-alien (&whole whole info alien)
- (let ((value (gensym))
- (info (if (and (consp info)
- (eq (car info) 'quote))
- (second info)
- (error "Something is wrong; local-alien-info not found: ~S"
- whole))))
- (values nil
- nil
- (list value)
- (if c:*converting-for-interpreter*
- `(%set-local-alien ',info ,alien ,value)
- `(if (%local-alien-forced-to-memory-p ',info)
- (%set-local-alien ',info ,alien ,value)
- (setf ,alien
- (deport ,value ',(local-alien-info-type info)))))
- whole)))
-
- (defun %local-alien-forced-to-memory-p (info)
- (local-alien-info-force-to-memory-p info))
-
- (defun %local-alien-addr (info alien)
- (declare (type local-alien-info info))
- (unless (local-alien-info-force-to-memory-p info)
- (error "~S isn't forced to memory. Something went wrong." alien))
- alien)
-
- (defun dispose-local-alien (info alien)
- (declare (ignore info))
- #+nil
- (cancel-finalization info)
- (free-alien alien))
-
-
- ;;;; The ADDR macro.
-
- (defmacro addr (expr &environment env)
- "Return an Alien pointer to the data addressed by Expr, which must be a call
- to SLOT or DEREF, or a reference to an Alien variable."
- (let ((form (macroexpand expr env)))
- (or (typecase form
- (cons
- (case (car form)
- (slot
- (cons '%slot-addr (cdr form)))
- (deref
- (cons '%deref-addr (cdr form)))
- (%heap-alien
- (cons '%heap-alien-addr (cdr form)))
- (local-alien
- (let ((info
- (let ((info-arg (second form)))
- (and (consp info-arg)
- (eq (car info-arg) 'quote)
- (second info-arg)))))
- (unless (local-alien-info-p info)
- (error "Something is wrong, local-alien-info not found: ~S"
- form))
- (setf (local-alien-info-force-to-memory-p info) t))
- (cons '%local-alien-addr (cdr form)))))
- (symbol
- (let ((kind (info variable kind form)))
- (when (eq kind :alien)
- `(%heap-alien-addr ',(info variable alien-info form))))))
- (error "~S is not a valid L-value" form))))
-
-
- ;;;; The CAST macro.
-
- (defmacro cast (alien type)
- "Convert ALIEN to an Alien of the specified TYPE (not evaluated.) Both types
- must be Alien array, pointer or function types."
- `(%cast ,alien ',(parse-alien-type type)))
-
- (defun %cast (alien target-type)
- (declare (type alien-value alien)
- (type alien-type target-type)
- (optimize-interface (safety 2))
- (optimize (inhibit-warnings 3)))
- (if (or (alien-pointer-type-p target-type)
- (alien-array-type-p target-type)
- (alien-function-type-p target-type))
- (let ((alien-type (alien-value-type alien)))
- (if (or (alien-pointer-type-p alien-type)
- (alien-array-type-p alien-type)
- (alien-function-type-p alien-type))
- (naturalize (alien-value-sap alien) target-type)
- (error "~S cannot be casted." alien)))
- (error "Cannot cast to alien type ~S" (unparse-alien-type target-type))))
-
-
-
- ;;;; The ALIEN-SIZE macro.
-
- (defmacro alien-size (type &optional (units :bits))
- "Return the size of the alien type TYPE. UNITS specifies the units to
- use and can be either :BITS, :BYTES, or :WORDS."
- (let* ((alien-type (parse-alien-type type))
- (bits (alien-type-bits alien-type)))
- (if bits
- (values (ceiling bits
- (ecase units
- (:bits 1)
- (:bytes vm:byte-bits)
- (:words vm:word-bits))))
- (error "Unknown size for alien type ~S."
- (unparse-alien-type alien-type)))))
-
-
-
- ;;;; Naturalize, deport, extract-alien-value, deposit-alien-value
-
- (defun naturalize (alien type)
- (declare (type alien-type type))
- (funcall (coerce (compute-naturalize-lambda type) 'function)
- alien type))
-
- (defun deport (value type)
- (declare (type alien-type type))
- (funcall (coerce (compute-deport-lambda type) 'function)
- value type))
-
- (defun extract-alien-value (sap offset type)
- (declare (type system-area-pointer sap)
- (type unsigned-byte offset)
- (type alien-type type))
- (funcall (coerce (compute-extract-lambda type) 'function)
- sap offset type))
-
- (defun deposit-alien-value (sap offset type value)
- (declare (type system-area-pointer sap)
- (type unsigned-byte offset)
- (type alien-type type))
- (funcall (coerce (compute-deposit-lambda type) 'function)
- sap offset type value))
-
-
-
- ;;;; alien-funcall, def-alien-function
-
- (defun alien-funcall (alien &rest args)
- "Call the foreign function ALIEN with the specified arguments. ALIEN's
- type specifies the argument and result types."
- (declare (type alien-value alien))
- (let ((type (alien-value-type alien)))
- (typecase type
- (alien-pointer-type
- (apply #'alien-funcall (deref alien) args))
- (alien-function-type
- (unless (= (length (alien-function-type-arg-types type))
- (length args))
- (error "Wrong number of arguments for ~S~%Expected ~D, got ~D."
- type
- (length (alien-function-type-arg-types type))
- (length args)))
- (let ((stub (alien-function-type-stub type)))
- (unless stub
- (setf stub
- (let ((fun (gensym))
- (parms (loop repeat (length args) collect (gensym))))
- (compile nil
- `(lambda (,fun ,@parms)
- (declare (type (alien ,type) ,fun))
- (alien-funcall ,fun ,@parms)))))
- (setf (alien-function-type-stub type) stub))
- (apply stub alien args)))
- (t
- (error "~S is not an alien function." alien)))))
-
- (defmacro def-alien-routine (name result-type &rest args)
- "Def-C-Routine Name Result-Type
- {(Arg-Name Arg-Type [Style])}*
-
- Define a foreign interface function for the routine with the specified Name,
- which may be either a string, symbol or list of the form (symbol string).
- Return-Type is the Alien fypte for the function return value. VOID may be
- used to specify a function with no result.
-
- The remaining forms specifiy individual arguments that are passed to the
- routine. Arg-Name is a symbol that names the argument, primarily for
- documentation. Arg-Type is the C-Type of the argument. Style specifies the
- say that the argument is passed.
-
- :IN
- An :In argument is simply passed by value. The value to be passed is
- obtained from argument(s) to the interface function. No values are
- returned for :In arguments. This is the default mode.
-
- :OUT
- The specified argument type must be a pointer to a fixed sized object.
- A pointer to a preallocated object is passed to the routine, and the
- the object is accessed on return, with the value being returned from
- the interface function. :OUT and :IN-OUT cannot be used with pointers
- to arrays, records or functions.
-
- :COPY
- Similar to :IN, except that the argument values are stored in on
- the stack, and a pointer to the object is passed instead of
- the values themselves.
-
- :IN-OUT
- A combination of :OUT and :COPY. A pointer to the argument is passed,
- with the object being initialized from the supplied argument and
- the return value being determined by accessing the object on return."
- (multiple-value-bind
- (lisp-name alien-name)
- (pick-lisp-and-alien-names name)
- (collect ((docs) (lisp-args) (arg-types) (alien-vars)
- (alien-args) (results))
- (dolist (arg args)
- (if (stringp arg)
- (docs arg)
- (destructuring-bind (name type &optional (style :in)) arg
- (unless (member style '(:in :copy :out :in-out))
- (error "Bogus argument style ~S in ~S." style arg))
- (unless (eq style :out)
- (lisp-args name))
- (cond ((eq style :in)
- (arg-types type)
- (alien-args name))
- (t
- (arg-types `(* ,type))
- (if (eq style :out)
- (alien-vars `(,name ,type))
- (alien-vars `(,name ,type ,name)))
- (alien-args `(addr ,name))))
- (when (or (eq style :out) (eq style :in-out))
- (results name)))))
- `(defun ,lisp-name ,(lisp-args)
- ,@(docs)
- (with-alien
- ((,lisp-name (function ,result-type ,@(arg-types))
- :extern ,alien-name)
- ,@(alien-vars))
- ,(if (alien-values-type-p result-type)
- (let ((temps (loop
- repeat (length (alien-values-type-values
- result-type))
- collect (gensym))))
- `(multiple-value-bind
- ,temps
- (alien-funcall ,lisp-name ,@(alien-args))
- (values ,@temps ,@(results))))
- `(values (alien-funcall ,lisp-name ,@(alien-args))
- ,@(results))))))))
-